#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <cassert>
#include <cstring>
static int defaultBuffer = 1024;//buffer的初始大小
class Buffer
{
public:
    Buffer()
        :_readerIdx(0)
        ,_writerIdx(0)
        ,_buffer(defaultBuffer)
    {}
public:
    //获取当前写入的起始地址
    char* WritePosition() { return &_buffer[_writerIdx]; }
    //获取当前读取的起始地址
    char* ReadPosition() { return &_buffer[_readerIdx]; }
    //获取可读数据的大小
    uint64_t ReadAbleSize() { return _writerIdx - _readerIdx; }
    //将读偏移向后移动
    void MoveReadOffset(uint64_t len)
    {
        if(len == 0) return;
        assert(len <= ReadAbleSize());
        _readerIdx += len;
    }
    //将写偏移向后移动
    void MoveWriteOffset(uint64_t len)
    {
        assert(len <= TailIdleSize());//向后移动的大小，必须小于等于后边剩余的空间大小
        _writerIdx += len;
    }
    //写入并移动void*类型数据
    void WriteAndPush(const void* data, uint64_t len)
    {
        Write(data, len);
        MoveWriteOffset(len);
    }
    //写入并移动std::string类型数据
    void WriteStringAndPush(const std::string& data)
    {
        WriteString(data);
        MoveWriteOffset(data.size());
    }
    //拷贝构造一个buffer
    void WriteBufferAndPush(Buffer& data)
    {
        WriteBuffer(data);
        MoveWriteOffset(data.ReadAbleSize());
    }
    //void*读取和弹出数据
    void ReadAndPop(void* buf, uint64_t len)
    {
        Read(buf, len);//把缓冲区的数据当成void*，进行读取
        MoveReadOffset(len);//将读偏移向后移动
    }
    //std::string读取和弹出数据
    std::string ReadAsStringAndPop(uint64_t len)
    {
        assert(len <= ReadAbleSize());
        std::string str = ReadAsString(len);//把缓冲区的数据当成std::string，进行读取
        MoveReadOffset(len);//将读偏移向后移动
        return str;
    }
    //获取一行数据（适配http等ascii协议）
    char* FindCRLF()//查找回车和换行
    {
        char* res = (char*)memchr(ReadPosition(), '\n', ReadAbleSize());//查找换行字符的地址
        return res;
    }
    std::string GetLineAndPop()
    {
        std::string str = GetLine();
        MoveReadOffset(str.size());//将读偏移向后移动
        return str;
    }
    //清空缓冲区（移动读写下标到初始位置）
    void Clear() { _readerIdx = _writerIdx = 0; }
private:
    //获取缓冲区末尾空闲空间大小——写下标之后的空闲空间
    uint64_t TailIdleSize() { return _buffer.size() - _writerIdx; }
    //获取缓冲区头部空间大小——读下标之前的空闲空间
    uint64_t HeadIdleSize() { return _readerIdx; }
    //确保可写空间足够
    void EnsureWriteSpace(uint64_t len)
    {
        if(TailIdleSize() >= len)//末尾的空间是否足够
        {
            return;
        }
        if(TailIdleSize() + HeadIdleSize() >= len)//起始+末尾的空间是否足够
        {
            //挪动数据到vector的开头
            uint64_t readSize = ReadAbleSize();
            std::copy(ReadPosition(), ReadPosition() + readSize, _buffer.data());//把可读数据拷贝到起始位置
            _readerIdx = 0;//将读偏移归0
            _writerIdx = readSize;//将写位置置为可读数据大小
        }
        else//整体空间不足，需要扩容
        {
            _buffer.resize(_writerIdx + len);
        }
    }
    //写入void*数据
    void Write(const void* data, uint64_t len)
    {
        if (len == 0) return;
        EnsureWriteSpace(len);//保证有足够空间
        std::copy(reinterpret_cast<const char*>(data), reinterpret_cast<const char*>(data) + len, WritePosition());//data是void*类型，没有步长
    }
    //写入std::string的接口
    void WriteString(const std::string& data) { Write(data.c_str(), data.size()); }
    //写入buffer类型的数据
    void WriteBuffer(Buffer& data) { Write(data.ReadPosition(), data.ReadAbleSize()); }
    //把缓冲区的数据当成void*，进行读取
    void Read(void* buf, uint64_t len)
    {
        //要求要获取的数据大小必须小于等于可读数据大小
        assert(len <= ReadAbleSize());
        std::copy(ReadPosition(), ReadPosition() + len, reinterpret_cast<char*>(buf));
    }
    //把缓冲区的数据当成std::string，进行读取
    std::string ReadAsString(uint64_t len)
    {
        //要求要获取的数据大小必须小于可读数据大小
        assert(len <= ReadAbleSize());
        std::string str;
        str.resize(len);
        Read(&str[0], len);
        return str;
    }
    std::string GetLine()
    {
        char* pos = FindCRLF();
        if(pos == nullptr) return "";//没找到
        return ReadAsString(pos - ReadPosition() + 1);//返回的包括\n
    }
private:
    std::vector<char> _buffer;//读写缓冲区空间
    uint64_t _readerIdx;//读下标
    uint64_t _writerIdx;//写下标
};